const wrapCtx = (wrapper, ctx) => fn => async (...args) => {
    return await wrapper(fn, ctx)(...args)
  }

  const wrapCnx = (wrapper, ctx) => fn => (...args) => {
    let f = async () => await fn(...args)
    return wrapper(f, ctx)
  }

  const genWrap = wp => (wrappers, ctx) => fn => {
    let f = fn
    for (let w of wrappers.reverse()) {
      f = wp(w, ctx)(f)
    }
    return f
  }
  // return wrappers.reduce((acc, w) => wrapCtx(w, ctx)(acc), fn)

  wrapX = (wrappers, ctx) => fn => {
    let f = fn
    for (let w of wrappers.reverse()) {
      if (typeof w === 'function') {
        f = wrapCnx(w, ctx)(f)
      } else if (w instanceof Array) {
        f = wrapCtx(w[0], ctx)(f)
      }
    }
    return f
  }

  const wrapM = genWrap(wrapCtx)
  const wrapN = genWrap(wrapCnx)

  module.exports = { wrapM, wrapN, wrapX }

  /*
  const id = (...x) => {
    console.log('id', x)
    return x
  }

  genN = x => (fn, ctx) => {
    console.log(`<--${x}`, ctx)
    let a = fn()
    console.log(`-->${x}`, ctx)
    return a
  }

  ;(async function(){
    let x = wrapN([
      genN(10),
      genN(20),
      genN(30)
    ], {})(id)
    //console.log(x)
    let y = await x(1, 2, 3)
    console.log('3=', y)
  })()


  genMiddleware = x => (fn, ctx) => async (...args) => {
    console.log(`<--${x}`, ctx, args)
    let a = await fn(...args)
    console.log(`-->${x}`, ctx)
    return a
  }

    ; (async function () {
      let a = wrapCtx(genMiddleware(111), {})(id)
      let b = await a(1, 2, 3)
      console.log('1=', b)
    })()

    ; (async function () {
      let x = wrapM([
        genMiddleware(1),
        genMiddleware(2),
        genMiddleware(3)
      ], {})(id)
      //console.log(x)
      let y = await x(1, 2, 3)
      console.log('2=', y)
    })()
  */